package algorithm;

import java.util.LinkedList;

import chart.Chart;

import board.Board;
import board.Board.Way;
import board.BoardEx;
import interfaces.Info;

public class AlgorithmEasy extends Algorithm {
	private Chart chart;

	private LinkedList<Board> boardList = new LinkedList<Board>();

	public AlgorithmEasy() {

		this.steps = new LinkedList<Board.Way>();

		this.lastState = null;
		this.steps.clear();
		// ustwianie listy kierunków
		this.directions = new LinkedList<Board.Way>();
		this.directions.clear();
		this.directions.add(Board.Way.LEFT);
		this.directions.add(Board.Way.RIGHT);
		this.directions.add(Board.Way.UP);
		this.directions.add(Board.Way.DOWN);
		this.finishState = null;

		this.chart = new Chart();
	}

	@Override
	public Info solve(Board initialState) {

		this.finishState = new Board(initialState.getHeight(),
				initialState.getWidth());

		// inicjalizacja algorytmu
		int quality;
		Board lastState = initialState;
		Board.Way lastWay = null;

		// licznik czasu
		long startTime = System.currentTimeMillis();
		try {
			LinkedList<Integer> qualityListEwe = new LinkedList<Integer>();

			// Golna czesc algorytmu
			while (!lastState.isDone()) {

				quality = -1;
				Board.Way where = null;
				LinkedList<Integer> qualityList = new LinkedList<Integer>();

				for (Board.Way iterable_way : directions) {

					// Czy czasen nie chce wrocic dotegosamego
					if (!opositeWay(iterable_way, lastWay)) {
						// czy mozna tam isc
						if (lastState.canMove(iterable_way)) {
							Board dataNext = lastState.move(iterable_way);

							// i sprawdzamy czy sie oplaca tam isc
							int newQuality = checkQuality(dataNext);

							if ((where == null)
									//|| (!isThere(boardList, dataNext))
									|| (quality > newQuality) && (!isThere(boardList, dataNext))) {

								where = iterable_way;
								quality = newQuality;

							}

						}
					}
					
				}
				if (quality != -1)
					qualityList.add(quality);
				System.out.println("Minimaln: " + min(qualityList));
				System.out.println("Maksymalne: " + max(qualityList));
				System.out.println("Średnie: " + mean(qualityList));
				System.out.println();
				chart.addToChart(steps.size(),quality);

				qualityList.clear();
				if (where == null)
					throw new AlgorithmEx(
							"Przerwane liczenie bo nigdzie się nie przesunoł");

				lastWay = where;
				steps.add(where);
				lastState = lastState.move(where);

				boardList.add(lastState);// takie na teraz żeby sprawdzić czy są

				// System.out.println("Jakość rozwiązania: " + quality);
				//lastState.wyswietl();

				actualizeState(lastState);

			}
		} catch (AlgorithmEx e) {
			System.out.println(e.getMessage());
			return new Info(System.currentTimeMillis() - startTime,
					steps.size(), false, steps);
		}

		chart.createChart();
		return new Info(System.currentTimeMillis() - startTime, steps.size(),
				true, steps);

	}

	/**
	 * Sprawdza czy czasem nie waraca
	 * 
	 * @param iterable_way
	 *            droga w ktora chce isc
	 * @param lastWay
	 *            poprzednia droga
	 * @return true jezel chce wrocic
	 */
	private boolean opositeWay(Board.Way iterable_way, Board.Way lastWay) {
		if (lastWay == null)
			return false;
		if (lastWay == Board.Way.DOWN && iterable_way == Board.Way.UP)
			return true;
		if (lastWay == Board.Way.UP && iterable_way == Board.Way.DOWN)
			return true;
		if (lastWay == Board.Way.LEFT && iterable_way == Board.Way.RIGHT)
			return true;
		if (lastWay == Board.Way.RIGHT && iterable_way == Board.Way.LEFT)
			return true;
		return false;
	}

	@Override
	protected int checkQuality(Board data) {
		int tmp = 0;
		int finish = 0;
		try {
			boolean linijka = true;
			for (int k = 0; k < finishState.getHeight(); ++k) {
				linijka = true;
				for (int m = 0; m < finishState.getWidth(); ++m) {
					finish = finishState.getValue(k, m);
					if (data.getValue(k, m) != finish) {
						tmp += 2;
						linijka = false;
					} else {
						
					}
					for (int i = 0; i < data.getHeight(); ++i) {
						for (int j = 0; j < data.getWidth(); ++j) {
							if (data.getValue(i, j) == finish)
								tmp += (Math.abs(k - i) + Math.abs(m - j));

						}
					}
				}
				
				 if(linijka == false) tmp+=10;
				 
			}
		} catch (BoardEx e) {
			System.out.println(e.getMessage());
		}

		return tmp;
	}

	/**
	 * Przeszukiwanie listy poprzednich kroków
	 * 
	 * @param boardList
	 *            lista zapisanych elemętów
	 * @param boardFind
	 *            do szukania
	 * @return Zwraca false jeśli nie ma
	 */
	private boolean isThere(LinkedList<Board> boardList, Board boardFind) {
		for (Board board : boardList) {
			if (board.isTheSame(boardFind) == true)
				return true;
		}
		return false;
	}

	/**
	 * Liczy minimalny elemęt listy
	 * 
	 * @param list
	 *            lista intów
	 * @return minimalny elemęt
	 */
	private int min(LinkedList<Integer> list) {
		if (list.size() != 0) {
			int temp = -1;
			for (Integer integer : list) {
				if (temp == -1 || integer < temp)
					temp = integer;
			}
			return temp;
		}
		return 0;
	}

	/**
	 * Liczy maksymalny elemęt listy intów
	 * 
	 * @param list
	 *            lista intów
	 * @return maksymalny elemęt
	 */
	private int max(LinkedList<Integer> list) {
		if (list.size() != 0) {
			int temp = -1;
			for (Integer integer : list) {
				if (integer > temp)
					temp = integer;
			}
			return temp;
		}
		return 0;
	}

	/**
	 * Liczenie średniej z listy intów
	 * 
	 * @param list
	 *            lista intów
	 * @return średnia
	 */
	private float mean(LinkedList<Integer> list) {
		if (list.size() != 0) {
			float temp = 0;
			for (Integer integer : list) {
				temp += integer;
			}
			return temp / list.size();
		}
		return 0;
	}
}
